﻿using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

namespace Memo.Electricity.Engine
{
	public abstract class WaitForSceneLoaded : CWaitable { }

	public class WaitForSceneLoadedSync : WaitForSceneLoaded
	{
		public WaitForSceneLoadedSync(string sceneName)
		{
			bool isDown = false;
			Action<Scene, LoadSceneMode> callback = null;
			callback = (Scene s, LoadSceneMode m) =>
			{
				if (s == SceneManager.GetSceneByName(sceneName))
				{
					isDown = true;
					CEventMgr.Inst.RemoveListener(EEventType.OnSceneLoaded, callback);
				}
			};

			CEventMgr.Inst.AddListener(EEventType.OnSceneLoaded, callback);

			ready = () => isDown;
		}
	}

	public class WaitForSceneLoadedAsync : WaitForSceneLoaded
	{
		public WaitForSceneLoadedAsync(AsyncOperation operation)
			=> ready = () => operation.isDone;
	}

	public class CSceneMgr : CSingletonBehaviour<CSceneMgr>
	{

		#region 对外接口
		//当前场景
		public Scene CurrentScene { get => SceneManager.GetActiveScene(); }
		//当前场景名称
		public string CurrentSceneName { get => CurrentScene.name; }

		/// <summary>
		/// 加载场景
		/// </summary>
		/// <param name="sceneName">场景名称</param>
		/// <param name="bFade">是否需要过渡动画</param>
		/// <param name="isAdditive">是否作为附加场景</param>
		/// <return>表示整个场景加载过程是否完成。当bFade=false，表示场景加载是否完成；当bFade=true, 表示场景加载与场景过渡动画是否均完成</return>
		public IWaitable LoadScene(string sceneName, bool bFade = true, bool isAdditive = false)
		{
			//已知Bug， bFade和isAdditive不能同时使用，不然特效播不出来
			if (bFade)
			{
				return CCoroutineMgr.Inst.StartCoroutine(LoadSceneWithFade(sceneName, isAdditive, SyncLoad));
			}
			else
			{
				return LoadSceneInternel(sceneName, isAdditive, SyncLoad);
			}
		}

		/// <summary>
		/// 异步加载场景
		/// </summary>
		/// <param name="sceneName">场景名称</param>
		/// <param name="bFade">是否需要过渡动画</param>
		/// <param name="isAdditive">是否作为附加场景</param>
		/// <return>表示整个场景加载过程是否完成。当bFade=false，表示场景加载是否完成；当bFade=true, 表示场景加载与场景过渡动画是否均完成</return>
		public IWaitable LoadSceneAsync(string sceneName, bool bFade = true, bool isAdditive = false)
		{
			if (bFade)
			{
				return CCoroutineMgr.Inst.StartCoroutine(LoadSceneWithFade(sceneName, isAdditive, AsyncLoad));
			}
			else
			{
				return LoadSceneInternel(sceneName, isAdditive, AsyncLoad);
			}
		}

		#endregion

		#region Event Handler

		private void OnActiveSceneChanged(Scene current, Scene next)
		{
			CEventMgr.Inst.Broadcast(EEventType.OnActiveSceneChanged, current, next);
		}

		private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
		{
			CEventMgr.Inst.Broadcast(EEventType.OnSceneLoaded, scene, mode);
		}

		private void OnSceneUnloaded(Scene current)
		{
			CEventMgr.Inst.Broadcast(EEventType.OnSceneUnloaded, current);
		}

		#endregion

		private void Awake()
		{
			//检查是否有重名场景
			if (!CheckRepetitiveSceneName())
				Destroy(gameObject);

			//注册事件
			SceneManager.activeSceneChanged += OnActiveSceneChanged;
			SceneManager.sceneLoaded += OnSceneLoaded;
			SceneManager.sceneUnloaded += OnSceneUnloaded;
		}

		//检查重复的场景名
		private bool CheckRepetitiveSceneName()
		{
			//检查是否有重名场景
			HashSet<string> sceneNames = new HashSet<string>();
			int sceneCount = SceneManager.sceneCountInBuildSettings;
			for (int i = 0; i < sceneCount; i++)
			{
				string name = SceneUtility.GetScenePathByBuildIndex(i);
				name = name.Substring(name.LastIndexOf("/"));
				if (sceneNames.Contains(name))
				{
					Debug.LogError($"检测到重复的场景名{name}，请检查");
					return false;
				}
			}
			return true;
		}

		#region Load Scene

		private delegate WaitForSceneLoaded LoadFunc(string name, LoadSceneMode mode);

		private IEnumerator LoadSceneWithFade(string sceneName, bool isAdditive, LoadFunc loadFunc)
		{
			yield return CSceneFadeEffect.PlayerSceneFadeEffect(Color.white, true);
			yield return new WaitForSeconds(0.2f);
			yield return LoadSceneInternel(sceneName, isAdditive, loadFunc);
			yield return CSceneFadeEffect.PlayerSceneFadeEffect(Color.white, false);
		}

		private WaitForSceneLoaded LoadSceneInternel(string sceneName, bool isAdditive, LoadFunc loadFunc)
		{
			if (isAdditive)
			{
				return loadFunc(sceneName, LoadSceneMode.Additive);
			}
			else
			{
				return loadFunc(sceneName, LoadSceneMode.Single);
			}
		}

		private WaitForSceneLoaded SyncLoad(string name, LoadSceneMode mode)
		{
			SceneManager.LoadScene(name, mode);
			return new WaitForSceneLoadedSync(name);
		}

		private WaitForSceneLoaded AsyncLoad(string name, LoadSceneMode mode)
		{
			//[TODO] 加载一个中间场景播放加载进度条
			return new WaitForSceneLoadedAsync(SceneManager.LoadSceneAsync(name, mode));
		}

		#endregion

	}

}
